home *** CD-ROM | disk | FTP | other *** search
/ Whiteline: Alpha / Whiteline Alpha.iso / linux / atari / source / source.lzh / atari-linux-0.01pl3 / fs / namei.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-05  |  15.0 KB  |  758 lines

  1. /*
  2.  *  linux/fs/namei.c
  3.  *
  4.  *  Copyright (C) 1991, 1992  Linus Torvalds
  5.  *
  6.  * This file is subject to the terms and conditions of the GNU General Public
  7.  * License.  See the file README.legal in the main directory of this archive
  8.  * for more details.
  9.  */
  10.  
  11. /*
  12.  * Some corrections by tytso.
  13.  */
  14.  
  15. #include <asm/segment.h>
  16.  
  17. #include <linux/errno.h>
  18. #include <linux/sched.h>
  19. #include <linux/kernel.h>
  20. #include <linux/string.h>
  21. #include <linux/fcntl.h>
  22. #include <linux/stat.h>
  23.  
  24. #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
  25.  
  26. /*
  27.  * In order to reduce some races, while at the same time doing additional
  28.  * checking and hopefully speeding things up, we copy filenames to the
  29.  * kernel data space before using them..
  30.  *
  31.  * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
  32.  */
  33. int getname(const char * filename, char **result)
  34. {
  35.     int error;
  36.     unsigned long i, page;
  37.     char * tmp, c;
  38.  
  39.     i = (unsigned long) filename;
  40.  
  41.     /* XXX */
  42. #ifdef __mc68000__
  43.     if (current->tss.crp[1] == task[0]->tss.crp[1]) {
  44. #else
  45.     if (0) {
  46. #endif
  47.         /* INIT TASK or child running in kernel address space */
  48.         i = PAGE_SIZE;
  49.         error = -ENAMETOOLONG;
  50.     } else {
  51.         if (!i || i >= TASK_SIZE)
  52.             return -EFAULT;
  53.         i = TASK_SIZE - i;
  54.         error = -EFAULT;
  55.     }
  56.     if (i > PAGE_SIZE) {
  57.         i = PAGE_SIZE;
  58.         error = -ENAMETOOLONG;
  59.     }
  60.     c = get_fs_byte(filename++);
  61.     if (!c)
  62.         return -ENOENT;
  63.     if(!(page = __get_free_page(GFP_KERNEL)))
  64.         return -ENOMEM;
  65.     *result = tmp = (char *) page;
  66.     while (--i) {
  67.         *(tmp++) = c;
  68.         c = get_fs_byte(filename++);
  69.         if (!c) {
  70.             *tmp = '\0';
  71.             return 0;
  72.         }
  73.     }
  74.     free_page(page);
  75.     return error;
  76. }
  77.  
  78. void putname(char * name)
  79. {
  80.     free_page((unsigned long) name);
  81. }
  82.  
  83. /*
  84.  *    permission()
  85.  *
  86.  * is used to check for read/write/execute permissions on a file.
  87.  * I don't know if we should look at just the euid or both euid and
  88.  * uid, but that should be easily changed.
  89.  */
  90. int permission(struct inode * inode,int mask)
  91. {
  92.     int mode = inode->i_mode;
  93.  
  94.     if (inode->i_op && inode->i_op->permission)
  95.         return inode->i_op->permission(inode, mask);
  96.     else if (current->euid == inode->i_uid)
  97.         mode >>= 6;
  98.     else if (in_group_p(inode->i_gid))
  99.         mode >>= 3;
  100.     if (((mode & mask & 0007) == mask) || suser())
  101.         return 1;
  102.     return 0;
  103. }
  104.  
  105. /*
  106.  * lookup() looks up one part of a pathname, using the fs-dependent
  107.  * routines (currently minix_lookup) for it. It also checks for
  108.  * fathers (pseudo-roots, mount-points)
  109.  */
  110. int lookup(struct inode * dir,const char * name, int len,
  111.     struct inode ** result)
  112. {
  113.     struct super_block * sb;
  114.     int perm;
  115.  
  116.     *result = NULL;
  117.     if (!dir)
  118.         return -ENOENT;
  119. /* check permissions before traversing mount-points */
  120.     perm = permission(dir,MAY_EXEC);
  121.     if (len==2 && name[0] == '.' && name[1] == '.') {
  122.         if (dir == current->root) {
  123.             *result = dir;
  124.             return 0;
  125.         } else if ((sb = dir->i_sb) && (dir == sb->s_mounted)) {
  126.             sb = dir->i_sb;
  127.             iput(dir);
  128.             dir = sb->s_covered;
  129.             if (!dir)
  130.                 return -ENOENT;
  131.             dir->i_count++;
  132.         }
  133.     }
  134.     if (!dir->i_op || !dir->i_op->lookup) {
  135.         iput(dir);
  136.         return -ENOTDIR;
  137.     }
  138.      if (!perm) {
  139.         iput(dir);
  140.         return -EACCES;
  141.     }
  142.     if (!len) {
  143.         *result = dir;
  144.         return 0;
  145.     }
  146.     return dir->i_op->lookup(dir,name,len,result);
  147. }
  148.  
  149. int follow_link(struct inode * dir, struct inode * inode,
  150.     int flag, int mode, struct inode ** res_inode)
  151. {
  152.     if (!dir || !inode) {
  153.         iput(dir);
  154.         iput(inode);
  155.         *res_inode = NULL;
  156.         return -ENOENT;
  157.     }
  158.     if (!inode->i_op || !inode->i_op->follow_link) {
  159.         iput(dir);
  160.         *res_inode = inode;
  161.         return 0;
  162.     }
  163.     return inode->i_op->follow_link(dir,inode,flag,mode,res_inode);
  164. }
  165.  
  166. /*
  167.  *    dir_namei()
  168.  *
  169.  * dir_namei() returns the inode of the directory of the
  170.  * specified name, and the name within that directory.
  171.  */
  172. static int dir_namei(const char * pathname, int * namelen, const char ** name,
  173.     struct inode * base, struct inode ** res_inode)
  174. {
  175.     char c;
  176.     const char * thisname;
  177.     int len,error;
  178.     struct inode * inode;
  179.  
  180.     *res_inode = NULL;
  181.     if (!base) {
  182.         base = current->pwd;
  183.         base->i_count++;
  184.     }
  185.     if ((c = *pathname) == '/') {
  186.         iput(base);
  187.         base = current->root;
  188.         pathname++;
  189.         base->i_count++;
  190.     }
  191.     while (1) {
  192.         thisname = pathname;
  193.         for(len=0;(c = *(pathname++))&&(c != '/');len++)
  194.             /* nothing */ ;
  195.         if (!c)
  196.             break;
  197.         base->i_count++;
  198.         error = lookup(base,thisname,len,&inode);
  199.         if (error) {
  200.             iput(base);
  201.             return error;
  202.         }
  203.         error = follow_link(base,inode,0,0,&base);
  204.         if (error)
  205.             return error;
  206.     }
  207.     if (!base->i_op || !base->i_op->lookup) {
  208.         iput(base);
  209.         return -ENOTDIR;
  210.     }
  211.     *name = thisname;
  212.     *namelen = len;
  213.     *res_inode = base;
  214.     return 0;
  215. }
  216.  
  217. static int _namei(const char * pathname, struct inode * base,
  218.     int follow_links, struct inode ** res_inode)
  219. {
  220.     const char * basename;
  221.     int namelen,error;
  222.     struct inode * inode;
  223.  
  224.     *res_inode = NULL;
  225.     error = dir_namei(pathname,&namelen,&basename,base,&base);
  226.     if (error)
  227.         return error;
  228.     base->i_count++;    /* lookup uses up base */
  229.     error = lookup(base,basename,namelen,&inode);
  230.     if (error) {
  231.         iput(base);
  232.         return error;
  233.     }
  234.     if (follow_links) {
  235.         error = follow_link(base,inode,0,0,&inode);
  236.         if (error)
  237.             return error;
  238.     } else
  239.         iput(base);
  240.     *res_inode = inode;
  241.     return 0;
  242. }
  243.  
  244. int lnamei(const char * pathname, struct inode ** res_inode)
  245. {
  246.     int error;
  247.     char * tmp;
  248.  
  249.     error = getname(pathname,&tmp);
  250.     if (!error) {
  251.         error = _namei(tmp,NULL,0,res_inode);
  252.         putname(tmp);
  253.     }
  254.     return error;
  255. }
  256.  
  257. /*
  258.  *    namei()
  259.  *
  260.  * is used by most simple commands to get the inode of a specified name.
  261.  * Open, link etc use their own routines, but this is enough for things
  262.  * like 'chmod' etc.
  263.  */
  264. int namei(const char * pathname, struct inode ** res_inode)
  265. {
  266.     int error;
  267.     char * tmp;
  268.  
  269.     error = getname(pathname,&tmp);
  270.     if (!error) {
  271.         error = _namei(tmp,NULL,1,res_inode);
  272.         putname(tmp);
  273.     }
  274.     return error;
  275. }
  276.  
  277. /*
  278.  *    open_namei()
  279.  *
  280.  * namei for open - this is in fact almost the whole open-routine.
  281.  *
  282.  * Note that the low bits of "flag" aren't the same as in the open
  283.  * system call - they are 00 - no permissions needed
  284.  *              01 - read permission needed
  285.  *              10 - write permission needed
  286.  *              11 - read/write permissions needed
  287.  * which is a lot more logical, and also allows the "no perm" needed
  288.  * for symlinks (where the permissions are checked later).
  289.  */
  290. int open_namei(const char * pathname, int flag, int mode,
  291.     struct inode ** res_inode, struct inode * base)
  292. {
  293.     const char * basename;
  294.     int namelen,error,i;
  295.     struct inode * dir, *inode;
  296.     struct task_struct ** p;
  297.  
  298.     mode &= S_IALLUGO & ~current->umask;
  299.     mode |= S_IFREG;
  300.     error = dir_namei(pathname,&namelen,&basename,base,&dir);
  301.     if (error)
  302.         return error;
  303.     if (!namelen) {            /* special case: '/usr/' etc */
  304.         if (flag & 2) {
  305.             iput(dir);
  306.             return -EISDIR;
  307.         }
  308.         /* thanks to Paul Pluzhnikov for noticing this was missing.. */
  309.         if (!permission(dir,ACC_MODE(flag))) {
  310.             iput(dir);
  311.             return -EACCES;
  312.         }
  313.         *res_inode=dir;
  314.         return 0;
  315.     }
  316.     for (i = 0; i < 5; i++) {    /* races... */
  317.         dir->i_count++;        /* lookup eats the dir */
  318.         error = lookup(dir,basename,namelen,&inode);
  319.         if (!error)
  320.             break;
  321.         if (!(flag & O_CREAT)) {
  322.             iput(dir);
  323.             return error;
  324.         }
  325.         if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
  326.             iput(dir);
  327.             return -EACCES;
  328.         }
  329.         if (!dir->i_op || !dir->i_op->create) {
  330.             iput(dir);
  331.             return -EACCES;
  332.         }
  333.         if (IS_RDONLY(dir)) {
  334.             iput(dir);
  335.             return -EROFS;
  336.         }
  337.         dir->i_count++;        /* create eats the dir */
  338.         error = dir->i_op->create(dir,basename,namelen,mode,res_inode);
  339.         if (error != -EEXIST) {
  340.             iput(dir);
  341.             return error;
  342.         }
  343.     }
  344.     if (error) {
  345.         iput(dir);
  346.         return error;
  347.     }
  348.     if ((flag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
  349.         iput(dir);
  350.         iput(inode);
  351.         return -EEXIST;
  352.     }
  353.     error = follow_link(dir,inode,flag,mode,&inode);
  354.     if (error)
  355.         return error;
  356.     if (S_ISDIR(inode->i_mode) && (flag & 2)) {
  357.         iput(inode);
  358.         return -EISDIR;
  359.     }
  360.     if (!permission(inode,ACC_MODE(flag))) {
  361.         iput(inode);
  362.         return -EACCES;
  363.     }
  364.     if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
  365.         if (IS_NODEV(inode)) {
  366.             iput(inode);
  367.             return -EACCES;
  368.         }
  369.     } else {
  370.         if (IS_RDONLY(inode) && (flag & 2)) {
  371.             iput(inode);
  372.             return -EROFS;
  373.         }
  374.     }
  375.      if ((inode->i_count > 1) && (flag & 2)) {
  376.          for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
  377.                 struct vm_area_struct * mpnt;
  378.              if (!*p)
  379.                  continue;
  380.              if (inode == (*p)->executable) {
  381.                  iput(inode);
  382.                  return -ETXTBSY;
  383.              }
  384.             for(mpnt = (*p)->mmap; mpnt; mpnt = mpnt->vm_next) {
  385.                 if (mpnt->vm_page_prot & PAGE_RW)
  386.                     continue;
  387.                 if (inode == mpnt->vm_inode) {
  388.                     iput(inode);
  389.                     return -ETXTBSY;
  390.                 }
  391.             }
  392.          }
  393.      }
  394.     if (flag & O_TRUNC) {
  395.           inode->i_size = 0;
  396.           if (inode->i_op && inode->i_op->truncate)
  397.                inode->i_op->truncate(inode);
  398.           if ((error = notify_change(NOTIFY_SIZE, inode))) {
  399.            iput(inode);
  400.            return error;
  401.           }
  402.           inode->i_dirt = 1;
  403.     }
  404.     *res_inode = inode;
  405.     return 0;
  406. }
  407.  
  408. int do_mknod(const char * filename, int mode, dev_t dev)
  409. {
  410.     const char * basename;
  411.     int namelen, error;
  412.     struct inode * dir;
  413.  
  414.     mode &= ~current->umask;
  415.     error = dir_namei(filename,&namelen,&basename, NULL, &dir);
  416.     if (error)
  417.         return error;
  418.     if (!namelen) {
  419.         iput(dir);
  420.         return -ENOENT;
  421.     }
  422.     if (IS_RDONLY(dir)) {
  423.         iput(dir);
  424.         return -EROFS;
  425.     }
  426.     if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
  427.         iput(dir);
  428.         return -EACCES;
  429.     }
  430.     if (!dir->i_op || !dir->i_op->mknod) {
  431.         iput(dir);
  432.         return -EPERM;
  433.     }
  434.     return dir->i_op->mknod(dir,basename,namelen,mode,dev);
  435. }
  436.  
  437. asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev)
  438. {
  439.     int error;
  440.     char * tmp;
  441.  
  442.     if (S_ISDIR(mode) || (!S_ISFIFO(mode) && !suser()))
  443.         return -EPERM;
  444.     switch (mode & S_IFMT) {
  445.     case 0:
  446.         mode |= S_IFREG;
  447.         break;
  448.     case S_IFREG: case S_IFCHR: case S_IFBLK: case S_IFIFO:
  449.         break;
  450.     default:
  451.         return -EINVAL;
  452.     }
  453.     error = getname(filename,&tmp);
  454.     if (!error) {
  455.         error = do_mknod(tmp,mode,dev);
  456.         putname(tmp);
  457.     }
  458.     return error;
  459. }
  460.  
  461. static int do_mkdir(const char * pathname, int mode)
  462. {
  463.     const char * basename;
  464.     int namelen, error;
  465.     struct inode * dir;
  466.  
  467.     error = dir_namei(pathname,&namelen,&basename,NULL,&dir);
  468.     if (error)
  469.         return error;
  470.     if (!namelen) {
  471.         iput(dir);
  472.         return -ENOENT;
  473.     }
  474.     if (IS_RDONLY(dir)) {
  475.         iput(dir);
  476.         return -EROFS;
  477.     }
  478.     if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
  479.         iput(dir);
  480.         return -EACCES;
  481.     }
  482.     if (!dir->i_op || !dir->i_op->mkdir) {
  483.         iput(dir);
  484.         return -EPERM;
  485.     }
  486.     return dir->i_op->mkdir(dir,basename,namelen,mode);
  487. }
  488.  
  489. asmlinkage int sys_mkdir(const char * pathname, int mode)
  490. {
  491.     int error;
  492.     char * tmp;
  493.  
  494.     error = getname(pathname,&tmp);
  495.     if (!error) {
  496.         error = do_mkdir(tmp,mode);
  497.         putname(tmp);
  498.     }
  499.     return error;
  500. }
  501.  
  502. static int do_rmdir(const char * name)
  503. {
  504.     const char * basename;
  505.     int namelen, error;
  506.     struct inode * dir;
  507.  
  508.     error = dir_namei(name,&namelen,&basename,NULL,&dir);
  509.     if (error)
  510.         return error;
  511.     if (!namelen) {
  512.         iput(dir);
  513.         return -ENOENT;
  514.     }
  515.     if (IS_RDONLY(dir)) {
  516.         iput(dir);
  517.         return -EROFS;
  518.     }
  519.     if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
  520.         iput(dir);
  521.         return -EACCES;
  522.     }
  523.     if (!dir->i_op || !dir->i_op->rmdir) {
  524.         iput(dir);
  525.         return -EPERM;
  526.     }
  527.     return dir->i_op->rmdir(dir,basename,namelen);
  528. }
  529.  
  530. asmlinkage int sys_rmdir(const char * pathname)
  531. {
  532.     int error;
  533.     char * tmp;
  534.  
  535.     error = getname(pathname,&tmp);
  536.     if (!error) {
  537.         error = do_rmdir(tmp);
  538.         putname(tmp);
  539.     }
  540.     return error;
  541. }
  542.  
  543. static int do_unlink(const char * name)
  544. {
  545.     const char * basename;
  546.     int namelen, error;
  547.     struct inode * dir;
  548.  
  549.     error = dir_namei(name,&namelen,&basename,NULL,&dir);
  550.     if (error)
  551.         return error;
  552.     if (!namelen) {
  553.         iput(dir);
  554.         return -EPERM;
  555.     }
  556.     if (IS_RDONLY(dir)) {
  557.         iput(dir);
  558.         return -EROFS;
  559.     }
  560.     if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
  561.         iput(dir);
  562.         return -EACCES;
  563.     }
  564.     if (!dir->i_op || !dir->i_op->unlink) {
  565.         iput(dir);
  566.         return -EPERM;
  567.     }
  568.     return dir->i_op->unlink(dir,basename,namelen);
  569. }
  570.  
  571. asmlinkage int sys_unlink(const char * pathname)
  572. {
  573.     int error;
  574.     char * tmp;
  575.  
  576.     error = getname(pathname,&tmp);
  577.     if (!error) {
  578.         error = do_unlink(tmp);
  579.         putname(tmp);
  580.     }
  581.     return error;
  582. }
  583.  
  584. static int do_symlink(const char * oldname, const char * newname)
  585. {
  586.     struct inode * dir;
  587.     const char * basename;
  588.     int namelen, error;
  589.  
  590.     error = dir_namei(newname,&namelen,&basename,NULL,&dir);
  591.     if (error)
  592.         return error;
  593.     if (!namelen) {
  594.         iput(dir);
  595.         return -ENOENT;
  596.     }
  597.     if (IS_RDONLY(dir)) {
  598.         iput(dir);
  599.         return -EROFS;
  600.     }
  601.     if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
  602.         iput(dir);
  603.         return -EACCES;
  604.     }
  605.     if (!dir->i_op || !dir->i_op->symlink) {
  606.         iput(dir);
  607.         return -EPERM;
  608.     }
  609.     return dir->i_op->symlink(dir,basename,namelen,oldname);
  610. }
  611.  
  612. asmlinkage int sys_symlink(const char * oldname, const char * newname)
  613. {
  614.     int error;
  615.     char * from, * to;
  616.  
  617.     error = getname(oldname,&from);
  618.     if (!error) {
  619.         error = getname(newname,&to);
  620.         if (!error) {
  621.             error = do_symlink(from,to);
  622.             putname(to);
  623.         }
  624.         putname(from);
  625.     }
  626.     return error;
  627. }
  628.  
  629. static int do_link(struct inode * oldinode, const char * newname)
  630. {
  631.     struct inode * dir;
  632.     const char * basename;
  633.     int namelen, error;
  634.  
  635.     error = dir_namei(newname,&namelen,&basename,NULL,&dir);
  636.     if (error) {
  637.         iput(oldinode);
  638.         return error;
  639.     }
  640.     if (!namelen) {
  641.         iput(oldinode);
  642.         iput(dir);
  643.         return -EPERM;
  644.     }
  645.     if (IS_RDONLY(dir)) {
  646.         iput(oldinode);
  647.         iput(dir);
  648.         return -EROFS;
  649.     }
  650.     if (dir->i_dev != oldinode->i_dev) {
  651.         iput(dir);
  652.         iput(oldinode);
  653.         return -EXDEV;
  654.     }
  655.     if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
  656.         iput(dir);
  657.         iput(oldinode);
  658.         return -EACCES;
  659.     }
  660.     if (!dir->i_op || !dir->i_op->link) {
  661.         iput(dir);
  662.         iput(oldinode);
  663.         return -EPERM;
  664.     }
  665.     return dir->i_op->link(oldinode, dir, basename, namelen);
  666. }
  667.  
  668. asmlinkage int sys_link(const char * oldname, const char * newname)
  669. {
  670.     int error;
  671.     char * to;
  672.     struct inode * oldinode;
  673.  
  674.     error = namei(oldname, &oldinode);
  675.     if (error)
  676.         return error;
  677.     error = getname(newname,&to);
  678.     if (error) {
  679.         iput(oldinode);
  680.         return error;
  681.     }
  682.     error = do_link(oldinode,to);
  683.     putname(to);
  684.     return error;
  685. }
  686.  
  687. static int do_rename(const char * oldname, const char * newname)
  688. {
  689.     struct inode * old_dir, * new_dir;
  690.     const char * old_base, * new_base;
  691.     int old_len, new_len, error;
  692.  
  693.     error = dir_namei(oldname,&old_len,&old_base,NULL,&old_dir);
  694.     if (error)
  695.         return error;
  696.     if (!permission(old_dir,MAY_WRITE | MAY_EXEC)) {
  697.         iput(old_dir);
  698.         return -EACCES;
  699.     }
  700.     if (!old_len || (old_base[0] == '.' &&
  701.         (old_len == 1 || (old_base[1] == '.' &&
  702.          old_len == 2)))) {
  703.         iput(old_dir);
  704.         return -EPERM;
  705.     }
  706.     error = dir_namei(newname,&new_len,&new_base,NULL,&new_dir);
  707.     if (error) {
  708.         iput(old_dir);
  709.         return error;
  710.     }
  711.     if (!permission(new_dir,MAY_WRITE | MAY_EXEC)) {
  712.         iput(old_dir);
  713.         iput(new_dir);
  714.         return -EACCES;
  715.     }
  716.     if (!new_len || (new_base[0] == '.' &&
  717.         (new_len == 1 || (new_base[1] == '.' &&
  718.          new_len == 2)))) {
  719.         iput(old_dir);
  720.         iput(new_dir);
  721.         return -EPERM;
  722.     }
  723.     if (new_dir->i_dev != old_dir->i_dev) {
  724.         iput(old_dir);
  725.         iput(new_dir);
  726.         return -EXDEV;
  727.     }
  728.     if (IS_RDONLY(new_dir) || IS_RDONLY(old_dir)) {
  729.         iput(old_dir);
  730.         iput(new_dir);
  731.         return -EROFS;
  732.     }
  733.     if (!old_dir->i_op || !old_dir->i_op->rename) {
  734.         iput(old_dir);
  735.         iput(new_dir);
  736.         return -EPERM;
  737.     }
  738.     return old_dir->i_op->rename(old_dir, old_base, old_len, 
  739.         new_dir, new_base, new_len);
  740. }
  741.  
  742. asmlinkage int sys_rename(const char * oldname, const char * newname)
  743. {
  744.     int error;
  745.     char * from, * to;
  746.  
  747.     error = getname(oldname,&from);
  748.     if (!error) {
  749.         error = getname(newname,&to);
  750.         if (!error) {
  751.             error = do_rename(from,to);
  752.             putname(to);
  753.         }
  754.         putname(from);
  755.     }
  756.     return error;
  757. }
  758.